﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel.Composition;
using System.Reactive.Concurrency;
using System.Reactive.Linq;
using Caliburn.Micro;
using Scrabble.Model;
using Scrabble.Tasks;

namespace Scrabble.ViewModels
{
    [Export(typeof(LexiconViewModel)), PartCreationPolicy(CreationPolicy.NonShared)]
    public class LexiconViewModel : PropertyChangedBase, IHandle<SearchRequest>
    {
        private readonly ObservableCollection<string> _searchResult = new ObservableCollection<string>();
        private readonly SearchOptionsViewModel _searchOptions;
        private string _searchText;
        private IDisposable _search;
        private bool _isSearchSuspended;

        [ImportingConstructor]
        public LexiconViewModel(IEventAggregator events)
        {
            _searchOptions = new SearchOptionsViewModel();
            _searchOptions.SearchOptionsChanged += OnSearchOptionsChanged;

            events.Subscribe(this);
        }

        public object SearchResult
        {
            get { return _searchResult; }
        }

        public string SearchText
        {
            get { return _searchText; }
            set
            {
                if (value == SearchText) return;

                _searchText = String.IsNullOrEmpty(value) ? String.Empty : value;
                NotifyOfPropertyChange(() => SearchText);
            }
        }

        public IResult ToggleOptionsPopup()
        {
            if (_searchOptions.IsActive)
            {
                _searchOptions.TryClose(true);
                return null;
            }

            return Show.Popup(_searchOptions);
        }

        public void UpdateSearch()
        {
            if (IsSearchSuspended) return;

            StopSearch();

            _searchResult.Clear();

            _search = FilteredWordList
                .ToObservable(Scheduler.ThreadPool)
                .Buffer(TimeSpan.FromMilliseconds(300))
                .ObserveOnDispatcher()
                .Subscribe(list => { foreach (var word in list) _searchResult.Add(word); });
        }

        public void StopSearch()
        {
            if (_search == null) return;
            _search.Dispose();
            _search = null;
        }

        public void Handle(SearchRequest message)
        {
            
            if (message.SearchWord == SearchText && message.SearchWordIsPrefix == _searchOptions.UseTextAsPrefix)
                return;

            IsSearchSuspended = true;

            SearchText = message.SearchWord;
            _searchOptions.UseTextAsPrefix = message.SearchWordIsPrefix;

            IsSearchSuspended = false;
        }

        [Import]
        private ILexicon WordList
        {
            get; set;
        }

        private bool IsSearchSuspended
        {
            get { return _isSearchSuspended; }
            set
            {
                if (value == IsSearchSuspended) return;

                _isSearchSuspended = value;

                if (IsSearchSuspended)
                    StopSearch();
                else
                    UpdateSearch();
            }
        }

        private IEnumerable<string> FilteredWordList
        {
            get
            {
                return String.IsNullOrWhiteSpace(SearchText)
                    ? WordList.AllWords
                    : (_searchOptions.UseTextAsPrefix
                        ? WordList.AllWordsStartingWith(SearchText)
                        : WordList.AllWordsComposedOf(SearchText));
            }
        }

        private void OnSearchOptionsChanged(object sender, EventArgs e)
        {
            if (_searchOptions.IsActive) _searchOptions.TryClose(true);
            if (! String.IsNullOrWhiteSpace(SearchText)) UpdateSearch();
        }
    }
}